<template>
  <CodeMirrorEditor
    class="mirror"
    ref="codeMirror"
    v-model:value="currValue"
    :options="options"
    border
    @change="handleChange"
    @dblclick="handleDbClick"
  />

  <el-dialog
    v-model="dialogVisible"
    :close-on-click-modal="false"
    :title="'编辑【' + props.title + '】'"
    :class="['code-mirror-dialog', props.title ? '' : 'hide-title']"
    destroy-on-close
    fullscreen
    append-to-body
    :before-close="beforeClose"
  >
    <CodeMirrorEditor
      v-if="props.mode !== 'groovy'"
      class="mirror"
      ref="dialogCodeMirror"
      v-model:value="dialogValue"
      :options="options"
      @change="changeValue"
      border
    />
    <div v-else class="box">
      <div class="left">
        <CodeMirrorEditor
          class="mirror"
          ref="dialogCodeMirror"
          v-model:value="dialogValue"
          :options="options"
          @cursorActivity="showHint"
          @change="changeValue"
          border
        />
      </div>
      <div class="right">
        <div class="tool">
          <el-collapse v-model="collapseName" accordion>
            <el-collapse-item
              v-for="(item, index) in tool"
              :key="index"
              :name="'tool_' + index"
              :title="item.text"
            >
              <div class="function-list">
                <div
                  class="function-item"
                  v-for="(fun, index) in item.children"
                  :key="index"
                >
                  <el-tooltip raw-content placement="left" effect="light">
                    <template #content>
                      <div class="content-box">
                        <div
                          class="content"
                          v-html="fun.data.description
                              .replace(/</ig, '&' + 'lt;')
                              .replace(/>/ig, '&' + 'gt;')
                              .replace(/\n/g, '<br/>')
                              .replace(/ {4}/g, '&nbsp;&nbsp;&nbsp;&nbsp;')"></div>
                      </div>
                    </template>
                    <div class="tooltip" @click="addCode(fun.data.code)">{{fun.data.name}}</div>
                  </el-tooltip>
                  <el-tooltip v-if="fun.data.example" raw-content>
                    <template #content>
                      <div class="content-box">
                        <div
                          class="content"
                          v-html="fun.data.example
                              .replace(/</ig, '&' + 'lt;')
                              .replace(/>/ig, '&' + 'gt;')
                              .replace(/\n/g, '<br/>')
                              .replace(/ {4}/g, '&nbsp;&nbsp;&nbsp;&nbsp;')"
                        ></div>
                      </div>
                    </template>
                    <div class="example" @click="addCode(fun.data.example)">示例</div>
                  </el-tooltip>
                </div>
              </div>
            </el-collapse-item>
          </el-collapse>
        </div>
        <div class="console">
          <el-tabs v-model="tabName">
            <el-tab-pane name="logger" label="日志">
              <el-input type="textarea" readonly v-model="debugLogger"></el-input>
            </el-tab-pane>
            <el-tab-pane name="result" label="结果">
              <el-input type="textarea" readonly v-model="debugResult"></el-input>
            </el-tab-pane>
          </el-tabs>
        </div>
        <div class="button">
          <el-button
            type="info"
            @click="cancel"
          >取消</el-button>
          <el-button
            type="success"
            @click="debug"
          >调试</el-button>
          <el-button
            type="primary"
            @click="confirm"
          >确定</el-button>
        </div>
      </div>
    </div>
    <template #footer v-if="props.mode !== 'groovy'">
      <el-button
        size="default"
        type="info"
        @click="cancel"
      >取消</el-button>
      <el-button
        size="default"
        type="primary"
        @click="confirm"
      >确定</el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, nextTick, watch, onBeforeMount, onMounted } from 'vue';
import CodeMirrorEditor from "codemirror-editor-vue3";
import "codemirror/mode/css/css.js";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/dracula.css";
import "codemirror/mode/vue/vue.js";
import "codemirror/mode/htmlmixed/htmlmixed.js";
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/groovy/groovy.js";
import "codemirror/addon/selection/active-line.js";
import "codemirror/addon/selection/mark-selection.js";
import "codemirror/addon/search/searchcursor.js";
import "codemirror/addon/scroll/annotatescrollbar.js";
import "codemirror/addon/search/matchesonscrollbar.js";
import "codemirror/addon/search/match-highlighter.js";
import "codemirror/mode/clike/clike.js";
import "codemirror/addon/edit/matchbrackets.js";
import "codemirror/addon/comment/comment.js";
import "codemirror/addon/dialog/dialog.js";
import "codemirror/addon/dialog/dialog.css";
import "codemirror/addon/search/search.js";
import "codemirror/keymap/sublime.js";
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/xml-fold.js";
import api from "@/http/api";
import code from "./code.js";
import { ElMessageBox } from "element-plus";

const emit = defineEmits(['update:modelValue', 'change', 'blur']);
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  mode: String,
  title: String,
  fullscreen: {
    type: Boolean,
    default: true
  },
});

const tool = ref([]);
const hintList = ref([]);
const codeMirror = ref();
const dialogCodeMirror = ref();
const currValue = ref(props.modelValue || '');
const dialogVisible = ref(false);
const dialogValue = ref('');
const dialogValueChange = ref(false);
const options = ref({});
const collapseName = ref('');
const tabName = ref('logger');
const debugLogger = ref('');
const debugResult = ref('');

const handleChange = () => {
  emit('update:modelValue', currValue.value);
  emit('change', currValue.value);
};

const handleDbClick = () => {
  if (!props.fullscreen) return;
  dialogValue.value = currValue.value;
  dialogVisible.value = true;
  dialogValueChange.value = false;
  nextTick(() => {
    dialogCodeMirror.value && dialogCodeMirror.value.refresh();
  });
};

const addCode = (code) => {
  if (code && dialogCodeMirror.value) {
    dialogCodeMirror.value.cminstance.replaceSelection(code);
    dialogCodeMirror.value.cminstance.focus();
  }
};

const beforeClose = (done) => {
  if (dialogValueChange.value) {
    ElMessageBox.confirm("内容有变更，请确认是否退出？", "提示", { type: "info" }).then(() => {
      done();
    });
  } else {
    done();
  }
};

const changeValue = () => {
  dialogValueChange.value = true;
};

const cancel = () => {
  if (dialogValueChange.value) {
    ElMessageBox.confirm("内容有变更，请确认是否退出？", "提示", { type: "info" }).then(() => {
      dialogVisible.value = false;
    });
  } else {
    dialogVisible.value = false;
  }
};

const confirm = () => {
  currValue.value = dialogValue.value;
  emit('update:modelValue', dialogValue.value);
  emit('change', dialogValue.value);
  dialogValueChange.value = false;
  dialogVisible.value = false;
};

const debug = () => {
  debugLogger.value = '';
  debugResult.value = '';

  api.post('/system/scriptGroovy/debugScript', {script: dialogValue.value}).then(res => {
    debugLogger.value = (res.data && res.data.logger || '').replace(/\n$/, '');
    debugResult.value = res.data && res.data.result || '';
  });
};

const showHint = () => {
  code.showHint(dialogCodeMirror.value.cminstance, hintList.value);
};

const loadToolData = (callback) => {
  api.post("/system/scriptTool/queryWithDetailTree").then(res => {
    tool.value = res.data && res.data.children || [];
    tool.value.forEach(item => {
      item.children.forEach(child => {
        if (child.data.code && child.data.code.indexOf('\n') === -1) {
          hintList.value.push(child.data.code);
        }
      });
    });
    callback();
  });
};

const refresh = () => {
  nextTick(() => codeMirror.value.refresh());
};

watch(() => props.modelValue, (newVal) => {
  currValue.value = newVal || '';
  nextTick(() => dialogValueChange.value = false);
});

onBeforeMount(() => {
  options.value = code.loadOptions();
});

onMounted(async() => {
  currValue.value = props.modelValue || '';
  hintList.value.push(...await code.loadModelHint());
  if (props.mode === 'groovy') {
    hintList.value.push(...await code.loadGroovyHint());
    hintList.value.push(...await code.loadDictionaryHint());
  }
  loadToolData(() => refresh());
  await nextTick(() => dialogValueChange.value = false);
});

defineExpose({ refresh });
</script>
<style lang="scss">
.code-mirror-dialog.hide-title .el-dialog__header {
  background: #0085f6;
  display: none!important;
}
.code-mirror-dialog .el-dialog__body {
  padding: 0!important;
  overflow: hidden!important;
}
.code-mirror-dialog .el-dialog__footer {
  position: absolute;
  right: 0;
  bottom: 0;
  border: none!important;
}
</style>

<style lang="scss" scoped>
.mirror {
  line-height: 25px;
  :deep(.cm-match-highlight) {
    background-color: #ffff0066;
  }
}
.mirror pre, .mirror div, .mirror span,
.mirror :deep(.CodeMirror pre.CodeMirror-line),
.mirror :deep(.CodeMirror pre.CodeMirror-line-like) {
  font-family: "Courier New", "Helvetica Neue", Helvetica, "PingFang SC",
  "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif!important;
}
:deep(.code-dialog) {
  .el-dialog__body {
    padding: 0!important;
  }
}
.box {
  display: flex;
  flex-direction: row;
  height: 100%;
  width: 100%;
  .left {
    width: 1px;
    flex-grow: 1;
    display: flex;
    flex-direction: column;
  }
  .right {
    width: 400px;
    padding: 0 0 0 10px;
    display: flex;
    flex-direction: column;
    .tool {
      height: 1px;
      overflow: auto;
      flex-grow: 1;
      .function-item {
        margin-left: 10px;
        font-size: 16px;
        display: flex;
        flex-direction: row;
        .tooltip {
          width: 1px;
          flex-grow: 1;
          flex-wrap: nowrap;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          cursor: pointer;
          color: #0055ff;
          margin-right: 10px;
        }
        .example {
          width: 50px;
          color: coral;
          font-weight: bold;
          cursor: pointer;
        }
      }
    }
    .console {
      height: 300px;
      display: flex;
      flex-direction: column;
      :deep(.el-tabs) {
        border-radius: 5px;
        padding: 0 10px;
        background: #282a36;
        display: flex;
        flex-direction: column;
        flex-grow: 1;
        .el-tabs__header {
          margin-bottom: 0!important;
          border-bottom: 1px solid #666;
          .el-tabs__nav {
            border: none;
            .el-tabs__item {
              padding: 0 20px;
              border: none;
              color: #999;
            }
            .el-tabs__item.is-active {
              color: #409eff;
              font-weight: 800;
            }
          }
          .el-tabs__active-bar {
            background-color: #409eff;
          }
          .el-tabs__nav-wrap::after {
            height: 1px;
          }

        }
        .el-tabs__content {
          height: 100%;
          .el-tab-pane, .el-textarea, .el-textarea__inner {
            height: 100%;
          }
          .el-textarea__inner {
            background: transparent;
            color: #f1fa8c!important;
            box-shadow: none;
            resize: none;
            padding: 5px 0;
            font-family: "Courier New", "Helvetica Neue", Helvetica, "PingFang SC",
              "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif!important;
          }
        }
      }
    }
    .button {
      padding: 5px 10px;
      text-align: right;
    }
  }
}

.content-box {
  .title {
    margin-bottom: 10px;
    font-size: 16px!important;
  }
  .content {
    font-size: 15px!important;
  }
}
</style>